////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_CONTAINERS_CALLBACK_INL__
#define __WN_CONTAINERS_CALLBACK_INL__

#ifndef __WN_CONTAINERS_CALLBACK_H__
    #error "WNCallback.inl should never be included directly. Please include WNCallback.h instead"
#endif

#include "WNCore/inc/WNTypes.h"
#include "WNCore/inc/WNAtomic.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMemory/inc/WNAllocation.h"
#include "WNContainers/inc/WNCallback.h"

namespace WNContainers {
    template <typename Return, typename Parameter>
    class __WNCallbackClasslessInternal1: public __WNCallbackBase1<Return, Parameter> {
    public:
        __WNCallbackClasslessInternal1(Return (*_function)(Parameter)) :
            __WNCallbackBase1<Return, Parameter>(),
            mFunction(_function) {
        }

        virtual Return Execute(Parameter _parameter) const {
            WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

            return(mFunction(_parameter));
        }

    private:
        Return (*mFunction)(Parameter);
    };

    template <typename Return, typename Parameter1, typename Parameter2>
    class __WNCallbackClasslessInternal2: public __WNCallbackBase2<Return, Parameter1, Parameter2> {
    public:
        __WNCallbackClasslessInternal2(Return (*_function)(Parameter1, Parameter2)) :
            __WNCallbackBase2<Return, Parameter1, Parameter2>(),
            mFunction(_function) {
        }

        virtual Return Execute(Parameter1 _parameter1, Parameter2 _parameter2) const {
            WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

            return(mFunction(_parameter1, _parameter2));
        }

    private:
        Return (*mFunction)(Parameter1, Parameter2);
    };

    template <typename Type, typename Return, typename Parameter>
    __WNCallbackInternal1<Type, Return, Parameter>::__WNCallbackInternal1(Return (Type::*_function)(Parameter), Type* _this) :
        __WNCallbackBase1<Return, Parameter>(),
        mThis(_this),
        mFunction(_function) {
    }

    template <typename Type, typename Return, typename Parameter>
    Return __WNCallbackInternal1<Type, Return, Parameter>::Execute(Parameter _parameter) const {
        WN_RELEASE_ASSERT_DESC(mThis != WN_NULL, "This pointer is null");
        WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

        return(mThis->mFunction(_parameter));
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::WNCallback1() {
        mBaseCallback = WN_NULL;
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::WNCallback1(const WNCallback1& _callback) {
        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::WNCallback1(Return (*_function)(Parameter)) {
        mBaseCallback = new __WNCallbackClasslessInternal1<Return, Parameter>(_function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::~WNCallback1() {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }
    }

    template<typename Return, typename Parameter>
    WNCallback1<Return, Parameter>& WNCallback1<Return, Parameter>::operator = (const WNCallback1& _callback) {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }

        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }

        return(*this);
    }

    template<typename Return, typename Parameter>
    Return WNCallback1<Return, Parameter>::Execute(Parameter _parameter) const {
        WN_DEBUG_ASSERT(mBaseCallback != WN_NULL);

        return(mBaseCallback->Execute(_parameter));
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    __WNCallbackBase2<Return, Parameter1, Parameter2>::__WNCallbackBase2() :
        mParameter(0) {
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WN_VOID __WNCallbackBase2<Return, Parameter1, Parameter2>::AddRef() {
        WNAtomicIncrement(&mParameter);
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WN_ATOM_T __WNCallbackBase2<Return, Parameter1, Parameter2>::RemoveRef() {
        WNAtomicDecrement(&mParameter);

        return(mParameter);
    }

    template <typename Type, typename Return, typename Parameter1, typename Parameter2>
    __WNCallbackInternal2<Type, Return, Parameter1, Parameter2>::__WNCallbackInternal2(Return (Type::*_function)(Parameter1, Parameter2), Type* _this) :
        __WNCallbackBase2<Return, Parameter1, Parameter2>(),
        mThis(_this),
        mFunction(_function) {
    }

    template <typename Type, typename Return, typename Parameter1, typename Parameter2>
    Return __WNCallbackInternal2<Type, Return, Parameter1, Parameter2>::Execute(Parameter1 _param, Parameter2 _param2) const {
        WN_RELEASE_ASSERT_DESC(mThis != WN_NULL, "This pointer is null");
        WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

        return(mThis->mFunction(_param, _param2));
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::WNCallback2() {
        mBaseCallback = WN_NULL;
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::WNCallback2(const WNCallback2& _callback) {
        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::WNCallback2(Return (*_function)(Parameter1, Parameter2)) {
        mBaseCallback = new __WNCallbackClasslessInternal2<Return, Parameter1, Parameter2>(_function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::~WNCallback2() {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>& WNCallback2<Return, Parameter1, Parameter2>::operator = (const WNCallback2& _callback) {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }

        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }

        return(*this);
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    Return WNCallback2<Return, Parameter1, Parameter2>::Execute(Parameter1 _param1, Parameter2 _param2) const {
        WN_DEBUG_ASSERT(mBaseCallback != WN_NULL);

        return(mBaseCallback->Execute(_param1, _param2));
    }
}

#endif // __WN_CONTAINERS_CALLBACK_INL__
